//
// Created by tang on 2022/10/24.
//

#include "texture.hpp"
#include <imgui/stb_image.h>
#include <utils/path_utils.hpp>
#include <cctype>

namespace GL
{
    GLenum GetChannelType(const std::string &filename) {
        GLenum result = GL_NONE;
        if (!filename.empty()) {
            auto pos = filename.rfind('.');
            if (pos != std::string::npos) {
                auto ext = filename.substr(pos);
                std::transform(begin(ext), end(ext), begin(ext), [](int ch) { return std::tolower(ch); });
                if (ext == ".jpg")
                    result = GL_RGB;
                else if (ext == ".png")
                    result = GL_RGBA;
            }
        }
        return result;
    }

    Texture::Texture(): m_handle_(0), m_width_(0), m_height_(0) {

    }

    Texture::Texture(const std::string &asset_name): Texture() {
        Load(asset_name);
    }

    Texture::~Texture() {
        Clean();
    }

    bool Texture::Load(const std::string &asset_name) {
        Clean();
        auto const type = GetChannelType(asset_name);
        if (type != GL_NONE) {
            int c;
            std::string full_path = (std::filesystem::path{ Utils::GetAssetDirPath() } / asset_name).string();
            stbi_uc* data = stbi_load(full_path.c_str(), &m_width_, &m_height_, &c, 0);
            if (data)
            {
                GLCallValidation(glGenTextures(1, &m_handle_));
                GLCallValidation(glBindTexture(GL_TEXTURE_2D, m_handle_));
                GLCallValidation(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
                GLCallValidation(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
                GLCallValidation(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
                GLCallValidation(glTexImage2D(GL_TEXTURE_2D, 0, type, m_width_, m_height_, 0, type, GL_UNSIGNED_BYTE, data));
                GLCallValidation(glBindTexture(GL_TEXTURE_2D, 0));
                stbi_image_free(data);
                return true;
            }
        }
        return false;
    }
    void* Texture::TexID() const {
        return (void*)(intptr_t)m_handle_;
    }

    glm::vec2 Texture::Size() const {
        return {(float )m_width_, (float)m_height_};
    }

    GLint Texture::Width() const {
        return m_width_;
    }
    GLint Texture::Height() const {
        return m_height_;
    }

    Texture::operator glm::vec2() const {
        return Size();
    }

    Texture::operator void*() const {
        return TexID();
    }

    Texture::operator unsigned int() const {
        return m_handle_;
    }

    void Texture::Clean() {
        if (m_handle_)
            GLCallValidation(glDeleteTextures(1, &m_handle_));
        m_handle_ = 0;
    }

    bool Texture::Upload(GLint width, GLint height, GLenum format, GLenum type, void *data) {
        if (m_handle_ == 0) {
            GLCallValidation(glGenTextures(1, &m_handle_));
            GLCallValidation(glBindTexture(GL_TEXTURE_2D, m_handle_));
            GLCallValidation(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
            GLCallValidation(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            GLCallValidation(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
            GLCallValidation(glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data));
            GLCallValidation(glBindTexture(GL_TEXTURE_2D, 0));
        } else {
            GLCallValidation(glBindTexture(GL_TEXTURE_2D, m_handle_));
            GLCallValidation(glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data));
            GLCallValidation(glBindTexture(GL_TEXTURE_2D, 0));
        }
        return true;
    }
    TexturePtr Texture::Create(const std::string &asset_name) {
        return std::make_shared<Texture>(asset_name);
    }
    TexturePtr Texture::Create() {
        return std::make_shared<Texture>();
    }

}
